package com.company.genealogy.common.filter;

import com.company.genealogy.common.constant.ApiCodeEnum;
import com.company.genealogy.common.constant.JwtConstants;
import com.company.genealogy.common.util.JwtUtil;
import com.company.genealogy.common.util.RedisUtil;
import com.company.genealogy.common.util.ResponseUtil;
import com.company.genealogy.web.entity.User;
import com.company.genealogy.web.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * JWT认证过滤器
 * 
 * 拦截所有HTTP请求，验证JWT token并设置Spring Security上下文。
 * 支持token自动刷新、黑名单检查、权限验证等功能。
 * 
 * 主要功能：
 * - 从请求头中提取JWT token
 * - 验证token的有效性和过期时间
 * - 检查token是否在黑名单中
 * - 设置Spring Security认证上下文
 * - 支持token自动刷新检测
 * 
 * @author chenzhixiang
 * @version 1.0
 * @since 2024-01-01
 */
@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        
        try {
            // 获取Authorization头
            String authorizationHeader = request.getHeader("Authorization");
            log.debug("JWT过滤器 - 请求路径: " + request.getRequestURI());
            log.debug("JWT过滤器 - Authorization头: " + (authorizationHeader != null ? "存在" : "不存在"));
            
            if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith(JwtConstants.BEARER_PREFIX)) {
                // 提取token
                String token = authorizationHeader.substring(JwtConstants.BEARER_PREFIX.length());
                log.debug("JWT过滤器 - 提取到token: " + (token != null ? "是" : "否"));
                
                // 验证token
                if (jwtUtil.validateToken(token)) {
                    log.debug("JWT过滤器 - token验证通过");
                    // 检查token是否在黑名单中
                    Long userId = jwtUtil.getUserIdFromToken(token);
                    log.debug("JWT过滤器 - 用户ID: " + userId);
                    
                    if (userId != null && !isTokenBlacklisted(userId, token)) {
                        log.debug("JWT过滤器 - token不在黑名单中，设置认证上下文");
                        // 设置认证上下文
                        setAuthenticationContext(token, userId);
                    } else {
                        log.debug("JWT过滤器 - token在黑名单中或用户ID为空");
                        // 返回401错误
//                        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
//                        response.getWriter().write("{\"code\":401,\"msg\":\"token已失效\",\"data\":null}");
                        ResponseUtil.error(response, ApiCodeEnum.UNAUTHORIZED);
                        return;
                    }
                } else {
                    log.debug("JWT过滤器 - token验证失败，返回401错误");
                    // 返回401错误，让前端能够检测到token过期
//                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
//                    response.setContentType("application/json;charset=UTF-8");
//                    response.getWriter().write("{\"code\":401,\"msg\":\"token已过期\",\"data\":null}");
                    ResponseUtil.error(response, ApiCodeEnum.UNAUTHORIZED);
                    return;
                }
            } else {
                log.debug("JWT过滤器 - 没有有效的Authorization头");
                // 对于需要认证的接口，返回401错误
                if (isProtectedEndpoint(request.getRequestURI())) {
//                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
//                    response.setContentType("application/json;charset=UTF-8");
//                    response.getWriter().write("{\"code\":401,\"msg\":\"缺少认证信息\",\"data\":null}");
                    ResponseUtil.error(response, ApiCodeEnum.UNAUTHORIZED);
                    return;
                }
            }
        } catch (Exception e) {
            log.error("JWT认证过滤器异常", e);
            // 发生异常时也返回401错误
//            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
//            response.setContentType("application/json;charset=UTF-8");
//            response.getWriter().write("{\"code\":401,\"msg\":\"认证异常\",\"data\":null}");
            ResponseUtil.error(response, ApiCodeEnum.UNAUTHORIZED);
            return;
        }
        
        // 继续过滤器链
        filterChain.doFilter(request, response);
    }

    /**
     * 判断是否为需要保护的端点
     */
    private boolean isProtectedEndpoint(String requestURI) {
        // 需要认证的API路径
        return requestURI.startsWith("/api/v1/") && 
               !requestURI.startsWith("/api/v1/auth/login") &&
               !requestURI.startsWith("/api/v1/auth/refresh");
    }

    /**
     * 检查token是否在黑名单中
     * 
     * @param userId 用户ID
     * @param token JWT token
     * @return true-在黑名单中，false-不在黑名单中
     */
    private boolean isTokenBlacklisted(Long userId, String token) {
        try {
            // 检查用户是否在黑名单中
            return redisUtil.isInBlacklist(userId);
        } catch (Exception e) {
            log.error("检查token黑名单失败", e);
            return false;
        }
    }

    /**
     * 设置Spring Security认证上下文
     * 
     * @param token JWT token
     * @param userId 用户ID
     */
    private void setAuthenticationContext(String token, Long userId) {
        try {
            // 获取用户详细信息
            UserDetails userDetails = userDetailsService.loadUserByUsername(userId.toString());
            
            if (userDetails != null) {
                // 从token中构建权限列表
                List<SimpleGrantedAuthority> authorities = buildAuthorities(token);
                
                // 创建认证token
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
                
                // 设置认证上下文
                SecurityContextHolder.getContext().setAuthentication(authentication);
                
                log.debug("JWT认证成功，用户ID: " + userId + ", 权限: " + authorities);
            }
        } catch (Exception e) {
            log.error("设置认证上下文失败", e);
        }
    }

    /**
     * 从token中构建权限列表
     * 
     * @param token JWT token
     * @return 权限列表
     */
    private List<SimpleGrantedAuthority> buildAuthorities(String token) {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        
        try {
            // 获取用户类型
            String userType = jwtUtil.getUserTypeFromToken(token);
            if (StringUtils.hasText(userType)) {
                authorities.add(new SimpleGrantedAuthority("ROLE_" + userType.toUpperCase()));
            }
            
            // 获取家族ID
            Long clanId = jwtUtil.getClanIdFromToken(token);
            if (clanId != null) {
                authorities.add(new SimpleGrantedAuthority("CLAN_" + clanId));
            }
            
            // 获取分支ID
            Long branchId = jwtUtil.getBranchIdFromToken(token);
            if (branchId != null) {
                authorities.add(new SimpleGrantedAuthority("BRANCH_" + branchId));
            }
            
            // 获取其他权限
            String authoritiesStr = jwtUtil.getAuthoritiesFromToken(token);
            if (StringUtils.hasText(authoritiesStr)) {
                String[] authArray = authoritiesStr.split(",");
                for (String auth : authArray) {
                    if (StringUtils.hasText(auth)) {
                        authorities.add(new SimpleGrantedAuthority(auth.trim()));
                    }
                }
            }
            
        } catch (Exception e) {
            log.error("构建权限列表失败", e);
        }
        
        return authorities;
    }

    /**
     * 检查token是否需要刷新
     * 
     * @param token JWT token
     * @return true-需要刷新，false-不需要刷新
     */
    private boolean shouldRefreshToken(String token) {
        try {
            // 检查token是否即将过期（比如还有5分钟过期）
            long expirationTime = jwtUtil.getExpirationDateFromToken(token).getTime();
            long currentTime = System.currentTimeMillis();
            long timeToExpire = expirationTime - currentTime;
            
            // 如果token还有不到5分钟过期，则建议刷新
            return timeToExpire < 5 * 60 * 1000;
        } catch (Exception e) {
            log.error("检查token刷新状态失败", e);
            return false;
        }
    }

    /**
     * 获取当前请求的token
     * 
     * @param request HTTP请求
     * @return token，如果不存在则返回null
     */
    public static Optional<String> getTokenFromRequest(HttpServletRequest request) {
        String authorizationHeader = request.getHeader("Authorization");
        if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith(JwtConstants.BEARER_PREFIX)) {
            return Optional.of(authorizationHeader.substring(JwtConstants.BEARER_PREFIX.length()));
        }
        return Optional.empty();
    }

    /**
     * 获取当前认证的用户ID
     * 
     * @return 用户ID，如果未认证则返回null
     */
    public static Optional<Long> getCurrentUserId() {
        if (SecurityContextHolder.getContext().getAuthentication() != null) {
            Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            if (principal instanceof UserDetails) {
                try {
                    return Optional.of(Long.parseLong(((UserDetails) principal).getUsername()));
                } catch (NumberFormatException e) {
                    return Optional.empty();
                }
            }
        }
        return Optional.empty();
    }
} 